using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using SpoonCore.Message.Info;
using SpoonCore.Message.Info.Core;

namespace SpoonCore
{
	public class MessageHandler
	{
		public delegate void OnDataReceiveHandler(byte[] data, IPEndPoint dest);
		public delegate void OnMessageReceiveHandler<T>(T msg, ClientInfo destClient) where T : IMessage;

		private readonly SpoonClient _client;
		private readonly SortedList<int, IMessage> _msgList = new SortedList<int, IMessage>();
		private readonly SortedList<int, IHandlerItem> _handlerList = new SortedList<int, IHandlerItem>();
		private readonly InfoMessageHandler _infoHandler;

		public MessageHandler(SpoonClient client)
		{
			_client = client;
			_infoHandler = new InfoMessageHandler(client);
		}

		public void ReceiveMessage(byte[] data, IPEndPoint source)
		{
			MemoryStream stream = new MemoryStream(data);
			BinaryReader br = new BinaryReader(stream);

			MessageInfo info = _infoHandler.DataReceive(br, source);

			ClientInfo clientInfo = new ClientInfo(_client, new SpoonIdentifier(br));
			clientInfo.AddEndpoint(source);

			int msgId = br.ReadInt32();

			if (_handlerList.ContainsKey(msgId))
			{
				IMessage msg = _msgList[msgId].Clone();
				msg.SetBytes(_client, br);
				msg.Info = info;
				_handlerList[msgId].DataReceive(msg, clientInfo);

				// add sender to list
				_client.AddClient(clientInfo);
			}
		}

		public byte[] GetBytes(IMessage msg, IPEndPoint endpoint)
		{
			MemoryStream stream = new MemoryStream();
			BinaryWriter bw = new BinaryWriter(stream);

			_infoHandler.DataSend(msg, endpoint);

			if(msg.Info != null)
			{
				bw.Write(msg.Info.GetBytes());
			}
			else bw.Write(MessageInfo.Default);

			bw.Write(_client.Id.GetBytes());
			bw.Write(msg.MsgId);
			bw.Write(msg.GetBytes());

			return stream.ToArray();
		}

		public void RegisterHandler<T>(OnMessageReceiveHandler<T> handler) where T : IMessage, new()
		{
			T item = new T();
			_handlerList.Add(item.MsgId, new HandlerItem<T>(handler));
			_msgList.Add(item.MsgId, item);
		}

		private interface IHandlerItem
		{
			void DataReceive(IMessage msg, ClientInfo client);
		}

		private class HandlerItem<T> : IHandlerItem where T : IMessage
		{
			public HandlerItem(OnMessageReceiveHandler<T> handler)
			{
				OnMessageReceive += handler;
			}

			private event OnMessageReceiveHandler<T> OnMessageReceive;

			public void DataReceive(IMessage msg, ClientInfo client)
			{
				if (OnMessageReceive != null) OnMessageReceive((T) msg, client);
			}
		}
	}
}